package gu.simplemq.redis;

import static com.google.common.base.Preconditions.checkArgument;
import static redis.clients.jedis.Protocol.*;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Protocol;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.util.JedisURIHelper;

/**
 * @author guyadong
 *
 */
public class JedisUtils {
	private JedisUtils() {}

	/**
	 * 创建连接redis的 URI,如'jedis://:123@myhost:6666/0'<br>
	 * 如果host,port,database参数为{@code null}则用默认值代替
	 * {@link Protocol#DEFAULT_HOST},{@link Protocol#DEFAULT_PORT},{@link Protocol#DEFAULT_DATABASE}
	 * @param scheme Scheme name,'jedis' as default if null 
	 * @param host 主机名
	 * @param port 端口号
	 * @param password 没有密码设为空或{@code null}
	 * @param database 数据库
	 * @return {@link URI}实例
	 */
	public static URI createJedisURI(String scheme,String host, Integer port,String password,Integer database){
		String userInfo = null;
		if (!Strings.isNullOrEmpty(password)) {
			userInfo = ":" + password;
		}
		try {
			return new URI(MoreObjects.firstNonNull(scheme, "jedis"), userInfo,
					MoreObjects.firstNonNull(host,DEFAULT_HOST),
					MoreObjects.firstNonNull(port,DEFAULT_PORT),
					"/" + MoreObjects.firstNonNull(database,DEFAULT_DATABASE),	
					null,null);
		} catch (URISyntaxException e) {
			throw new RuntimeException(e);
		}	
	}
	/**
	 * 创建schema为'jedis'的URI,如'jedis://:123@myhost:6666/0'<br>
	 * @param host 主机名
	 * @param port 端口号
	 * @param password 没有密码设为空或{@code null}
	 * @param database 数据库
	 * @return {@link URI}实例
	 * @see #createJedisURI(String, String, Integer, String, Integer)
	 */
	public static URI createJedisURI(String host, Integer port,String password,Integer database){
		return createJedisURI("jedis",host,port,password,database);
	}
	/**
	 * 根据连接参数创建一个{@link URI}对象
	 * @param parameters
	 * @return
	 */
	public static URI getCanonicalURI(Map<JedisPoolLazy.PropName,Object> parameters){
		parameters = JedisPoolLazy.initParameters(parameters);
		URI uri = (URI) parameters.get(JedisPoolLazy.PropName.uri);
		if (null == uri) {
			uri = createJedisURI((String) parameters.get(JedisPoolLazy.PropName.host),
					((Number) parameters.get(JedisPoolLazy.PropName.port)).intValue(),
					(String) parameters.get(JedisPoolLazy.PropName.password),
					((Number)parameters.get(JedisPoolLazy.PropName.database)).intValue());
		}else{
			try {
				uri = new URI("jedis", 
						uri.getUserInfo(), 
						convertHost(uri.getHost()),
						uri.getPort(), 
						"/" + JedisURIHelper.getDBIndex(uri), 
						null, 
						null);
			} catch (URISyntaxException e) {
				throw new RuntimeException(e);
			}	
		}
		return uri;
	}
	/**
	 * 执行 redis操作,
	 * @param fun redis操作
	 * @param poolLazy
	 * @return
	 */
	public static <T> T runOnRedis(Function<Jedis,T> fun,JedisPoolLazy poolLazy) {
		if(null == fun){
			return null;
		}
		Jedis jedis = poolLazy.apply();
		try{
			return fun.apply(jedis);
		}finally{
			poolLazy.free();
		}
	}
	public static <T> T runOnRedis(Function<Jedis,T> fun) {
		return runOnRedis(fun,JedisPoolLazy.getDefaultInstance());
	}
	private static String convertHost(String host) {
		if ("127.0.0.1".equals(host)) {
			return Protocol.DEFAULT_HOST;
		}
		else if ("::1".equals(host)) {
			return Protocol.DEFAULT_HOST;
		}
		
		return host;
	}

	/**
	 * 执行 {@link Jedis#setnx(String, String)} 设置{@code key}的值{@code value},如果{@code key}已经存在就返回原值<br>
	 * {@code key,value}不可为{@code null}
	 * @param key
	 * @param value
	 * @return
	 */
	public static String setnx(final String key, final String value){
		checkArgument(!Strings.isNullOrEmpty(key) && !Strings.isNullOrEmpty(value),"key or value is null or empty");
		return runOnRedis(new Function<Jedis,String>(){
			@Override
			public String apply(Jedis input) {
				input.setnx(key, value);
				return input.get(key);
			}});
	}
	/**
	 * 执行 {@link Jedis#get(String)} 返回{@code key}的值<br>
	 * @param key 不可为{@code null}
	 * @return
	 */
	public static String get(final String key){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		return runOnRedis(new Function<Jedis,String>(){
			@Override
			public String apply(Jedis input) {
				return input.get(key);
			}});
	}

	/**
	 * 将redis中{@code key}指定的变量步进加1并返回
	 * @param key 变量名,不可为{@code null}或空
	 * @return
	 * @see {@link Jedis#incr(String)}
	 */
	public static long incr(final String key){
		checkArgument(!Strings.isNullOrEmpty(key),"key is null or empty");
		return runOnRedis(new Function<Jedis,Long>(){
			@Override
			public Long apply(Jedis input) {
				return input.incr(key);
			}});
	}

	/**
	 * 测试接参数是否有效
	 * @return 连接失败返回{@code false}
	 */
	public static boolean testConnect(Map<JedisPoolLazy.PropName,Object> props){
		props = JedisPoolLazy.initParameters(props);
	    final Jedis jedis = new Jedis(
	    		(String)props.get(JedisPoolLazy.PropName.host), 
	    		((Number)props.get(JedisPoolLazy.PropName.port)).intValue(), 
	    		((Number)props.get(JedisPoolLazy.PropName.timeout)).intValue(),
	    		((Number)props.get(JedisPoolLazy.PropName.timeout)).intValue());
	
	    try {
	    	jedis.connect();
	    	String password = (String)props.get(JedisPoolLazy.PropName.password);
	    	if (null != password) {
	    		jedis.auth(password);
	    	}
	    	int database = (int)props.get(JedisPoolLazy.PropName.database);
	    	if (database != 0) {
	    		jedis.select(database);
	    	}
	    	return true;
	    } catch (JedisException je) {	      
	      return false;
	    }finally{
	    	jedis.close();
	    }
	}
	/**
	 * 测试{@link Jedis}连接是否有效<br>
	 * 测试方式参照 {@link redis.clients.jedis.JedisFactory#validateObject(org.apache.commons.pool2.PooledObject)}
	 * @param jedis 要测试的{@link Jedis}实例，为{@code null}时返回{@code false}
	 * @return 连接有效返回{@code true}，否则返回{@code false}
	 */
	public static boolean testConnect(Jedis jedis){
		return (jedis instanceof Jedis) && jedis.isConnected() && jedis.ping().equals("PONG");
	}
}
